home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / misc / sci / ephem_src_4_28.lha / io.c.orig < prev    next >
Text File  |  1992-04-17  |  22KB  |  823 lines

  1. /* this file (in principle) contains all the device-dependent code for
  2.  * handling screen movement and reading the keyboard. public routines are:
  3.  *   c_pos(r,c), c_erase(), c_eol();
  4.  *   chk_char(), read_char(), read_line (buf, max); and
  5.  *   byetty().
  6.  * N.B. we assume output may be performed by printf(), putchar() and
  7.  *   fputs(stdout). since these are buffered we flush first in read_char().
  8.  */
  9.  
  10. /* explanation of various conditional #define options:
  11.  * UNIX: uses termcap for screen management.
  12.  *   USE_TERMIO: use termio.h to control tty modes.
  13.  *   USE_SGTTY: use sgtty.h to control tty modes.
  14.  *   USE_NDELAY: do non-blocking tty reads with fcntl(O_NDELAY).
  15.  *   USE_FIONREAD: do non-blocking tty reads with ioctl(FIONREAD).
  16.  *   USE_ATTSELECT: do non-blocking reads with att's select(2) (4 args).
  17.  *   USE_BSDSELECT: do non-blocking reads with bsd's select(2) (5 args).
  18.  * TURBO_C: compiles for Turbo C 2.0. I'm told it works for Lattice and
  19.  *     Microsoft too.
  20.  *   USE_ANSISYS: default PC cursor control uses direct BIOS calls (thanks to
  21.  *     Mr. Doug McDonald). If your PC does not work with this, however, add
  22.  *     "device ANSI.SYS" to your config.sys file and build ephem with
  23.  *     USE_ANSISYS.
  24.  * VMS: uses QIO for input, TERMTABLE info for output. This code uses only
  25.  *     standard VMS calls, i.e. it does not rely on any third-vendor termcap
  26.  *     package or the like. The code includes recoqnition of arrow keys, it
  27.  *     is easy to extend it to recoqnize other function keys. you don't
  28.  *     need to #define VMS since it is inherent in the compiler.
  29.  */
  30.  
  31. /* unless you are on VMS define one of these... */
  32. #define UNIX
  33. /* #define TURBO_C */
  34.  
  35. /* then if you defined UNIX you must use one of these ways to do non-blocking
  36.  * tty reads
  37.  */
  38. #define USE_FIONREAD
  39. /* #define USE_NDELAY */
  40. /* #define USE_ATTSELECT */
  41. /* #define USE_BSDSELECT */
  42.  
  43. /* and then if you defined UNIX you must also use one of these ways to control
  44.  * the tty modes.
  45.  */
  46. #define USE_TERMIO
  47. /* #define USE_SGTTY */
  48.  
  49. /* if you defined TURBO_C you might want this too if screen io looks garbled */
  50. /* #define USE_ANSISYS */
  51.  
  52. #include <stdio.h>
  53. #include <ctype.h>
  54. #include "screen.h"
  55.  
  56. #ifdef UNIX
  57. #include <signal.h>
  58. #ifdef USE_TERMIO
  59. #include <termio.h>
  60. #endif
  61. #ifdef USE_SGTTY
  62. #include <sgtty.h>
  63. #endif
  64. #ifdef USE_BSDSELECT
  65. #include <sys/time.h>
  66. #endif
  67. #ifdef USE_NDELAY
  68. #include <fcntl.h>
  69. #endif
  70.  
  71. extern char *tgoto();
  72. static char *cm, *ce, *cl, *ks, *ke, *kl, *kr, *ku, *kd; /* termcap sequences */
  73. static int tloaded;
  74. static int ttysetup;
  75. #ifdef USE_TERMIO
  76. static struct termio orig_tio;
  77. #endif
  78. #ifdef USE_SGTTY
  79. static struct sgttyb orig_sgtty;
  80. #endif
  81.  
  82. /* move cursor to row, col, 1-based.
  83.  * we assume this also moves a visible cursor to this location.
  84.  */
  85. c_pos (r, c)
  86. int r, c;
  87. {
  88.     if (!tloaded) tload();
  89.     fputs (tgoto (cm, c-1, r-1), stdout);
  90. }
  91.  
  92. /* erase entire screen. */
  93. c_erase()
  94. {
  95.     if (!tloaded) tload();
  96.     fputs (cl, stdout);
  97. }
  98.  
  99. /* erase to end of line */
  100. c_eol()
  101. {
  102.     if (!tloaded) tload();
  103.     fputs (ce, stdout);
  104. }
  105.  
  106. #ifdef USE_NDELAY
  107. static char sav_char;    /* one character read-ahead for chk_char() */
  108. #endif
  109.  
  110. /* return 0 if there is a char that may be read without blocking, else -1 */
  111. chk_char()
  112. {
  113. #ifdef USE_NDELAY
  114.     if (!ttysetup) setuptty();
  115.     fflush (stdout);
  116.     if (sav_char)
  117.         return (0);
  118.     fcntl (0, F_SETFL, O_NDELAY);    /* non-blocking read. FNDELAY on BSD */
  119.     if (read (0, &sav_char, 1) != 1)
  120.         sav_char = 0;
  121.     return (sav_char ? 0 : -1);
  122. #endif
  123. #ifdef USE_ATTSELECT
  124.     int nfds, rfds, wfds, to;
  125.     if (!ttysetup) setuptty();
  126.     fflush (stdout);
  127.     rfds = 1 << 0;    /* reads are on fd 0 */
  128.     wfds = 0;    /* not interested in any write fds */
  129.     nfds = 1;    /* check only fd 0 */
  130.     to = 0;        /* don't delay - return 0 if nothing pending */
  131.     return (select (nfds, &rfds, &wfds, to) == 0 ? -1 : 0);
  132. #endif
  133. #ifdef USE_BSDSELECT
  134.     int nfds, rfds, wfds, xfds;
  135.     struct timeval to;
  136.     if (!ttysetup) setuptty();
  137.     fflush (stdout);
  138.     rfds = 1 << 0;    /* reads are on fd 0 */
  139.     wfds = 0;    /* not interested in any write fds */
  140.     xfds = 0;    /* not interested in any exception fds */
  141.     nfds = 1;    /* check only fd 0 */
  142.     to.tv_sec = 0;    /* don't delay - return 0 if nothing pending */
  143.     to.tv_usec = 0;    /* don't delay - return 0 if nothing pending */
  144.     return (select (nfds, &rfds, &wfds, &xfds, &to) == 0 ? -1 : 0);
  145. #endif
  146. #ifdef USE_FIONREAD
  147.     long n;
  148.     if (!ttysetup) setuptty();
  149.     fflush (stdout);
  150.     ioctl (0, FIONREAD, &n);
  151.     return (n > 0 ? 0 : -1);
  152. #endif
  153. }
  154.  
  155. /* read the next char, blocking if necessary, and return it. don't echo.
  156.  * map the arrow keys if we can too into hjkl
  157.  */
  158. read_char()
  159. {
  160.     char c;
  161.     if (!ttysetup) setuptty();
  162.     fflush (stdout);
  163. #ifdef USE_NDELAY
  164.     fcntl (0, F_SETFL, 0);    /* blocking read */
  165.     if (sav_char) {
  166.         c = sav_char;
  167.         sav_char = 0;
  168.     } else
  169. #endif
  170.         read (0, &c, 1);
  171.     c = chk_arrow (c & 0177); /* just ASCII, please */
  172.     return (c);
  173. }
  174.  
  175. /* used to time out of a read */
  176. static got_alrm;
  177. static
  178. on_alrm()
  179. {
  180.     got_alrm = 1;
  181. }
  182.  
  183. /* see if c is the first of any of the termcap arrow key sequences.
  184.  * if it is, read the rest of the sequence, and return the hjkl code
  185.  * that corresponds.
  186.  * if no match, just return c.
  187.  */
  188. static 
  189. chk_arrow (c)
  190. register char c;
  191. {
  192.     register char *seq;
  193.  
  194.     if (kl && kd && ku && kr &&
  195.         (c == *(seq = kl) || c == *(seq = kd) || c == *(seq = ku)
  196.                          || c == *(seq = kr))) {
  197.         char seqa[32]; /* maximum arrow escape sequence ever expected */
  198.         unsigned l = strlen(seq);
  199.         seqa[0] = c;
  200.         if (l > 1) {
  201.         extern unsigned alarm();
  202.         /* cautiously read rest of arrow sequence */
  203.         got_alrm = 0;
  204.         (void) signal (SIGALRM, on_alrm);
  205.         alarm(2);
  206.         read (0, seqa+1, l-1);
  207.         alarm(0);
  208.         if (got_alrm)
  209.             return (c);
  210.         }
  211.         seqa[l] = '\0';
  212.         if (strcmp (seqa, kl) == 0)
  213.         return ('h');
  214.         if (strcmp (seqa, kd) == 0)
  215.         return ('j');
  216.         if (strcmp (seqa, ku) == 0)
  217.         return ('k');
  218.         if (strcmp (seqa, kr) == 0)
  219.         return ('l');
  220.     }
  221.     return (c);
  222. }
  223.  
  224. /* do whatever might be necessary to get the screen and/or tty back into shape.
  225.  */
  226. byetty()
  227. {
  228.     /* if keypad mode switch is used, turn it off now */
  229.     if (ke) {
  230.         fputs (ke, stdout);
  231.         fflush (stdout);
  232.     }
  233.  
  234. #ifdef USE_TERMIO
  235.     ioctl (0, TCSETA, &orig_tio);
  236. #endif
  237. #ifdef USE_SGTTY
  238.     ioctl (0, TIOCSETP, &orig_sgtty);
  239. #endif
  240. #ifdef USE_NDELAY
  241.     fcntl (0, F_SETFL, 0);    /* be sure to go back to blocking read */
  242. #endif
  243.     ttysetup = 0;
  244. }
  245.  
  246. static 
  247. tload()
  248. {
  249.     extern char *getenv(), *tgetstr();
  250.     extern char *UP, *BC;
  251.     char *egetstr();
  252.     static char tbuf[512];
  253.     char rawtbuf[1024];
  254.     char *tp;
  255.     char *ptr;
  256.  
  257.     if (!(tp = getenv ("TERM"))) {
  258.         printf ("no TERM\n");
  259.         exit(1);
  260.     }
  261.     if (tgetent (rawtbuf, tp) != 1) {
  262.         printf ("Can't find termcap for %s\n", tp);
  263.         exit (1);
  264.     }
  265.  
  266.     ptr = tbuf;
  267.  
  268.     ku = egetstr ("ku", &ptr);
  269.     kd = egetstr ("kd", &ptr);
  270.     kl = egetstr ("kl", &ptr);
  271.     kr = egetstr ("kr", &ptr);
  272.  
  273.     if (!(cm = egetstr ("cm", &ptr))) {
  274.         printf ("No termcap cm code\n");
  275.         exit(1);
  276.     }
  277.     if (!(ce = egetstr ("ce", &ptr))) {
  278.         printf ("No termcap ce code\n");
  279.         exit(1);
  280.     }
  281.     if (!(cl = egetstr ("cl", &ptr))) {
  282.         printf ("No termcap cl code\n");
  283.         exit(1);
  284.     }
  285.  
  286.     UP = egetstr ("up", &ptr);
  287.     if (!tgetflag ("bs"))
  288.         BC = egetstr ("bc", &ptr);
  289.     
  290.     if (!ttysetup) setuptty();
  291.  
  292.     /* if keypad mode switch is used, do it now */
  293.     if (ks = egetstr ("ks", &ptr)) {
  294.         ke = egetstr ("ke", &ptr);
  295.         fputs (ks, stdout);
  296.         fflush (stdout);
  297.     }
  298.  
  299.     tloaded = 1;
  300. }
  301.  
  302. /* like tgetstr() but discard termcap delay codes, for now anyways */
  303. static char *
  304. egetstr (name, sptr)
  305. char *name;
  306. char **sptr;
  307. {
  308.     extern char *tgetstr();
  309.     char *s;
  310.  
  311.     if (s = tgetstr (name, sptr)) {
  312.         char c;
  313.         while ((c = *s) == '*' || isdigit(c))
  314.         s += 1;
  315.     }
  316.     return (s);
  317. }
  318.  
  319. /* set up tty for char-by-char read, non-blocking  */
  320. static
  321. setuptty()
  322. {
  323. #ifdef USE_TERMIO
  324.     struct termio tio;
  325.  
  326.     ioctl (0, TCGETA, &orig_tio);
  327.     tio = orig_tio;
  328.     tio.c_iflag &= ~ICRNL;    /* leave CR unchanged */
  329.     tio.c_oflag &= ~OPOST;    /* no output processing */
  330.     tio.c_lflag &= ~(ICANON|ECHO); /* no input processing, no echo */
  331.     tio.c_cc[VMIN] = 1;    /* return after each char */
  332.     tio.c_cc[VTIME] = 0;    /* no read timeout */
  333.     ioctl (0, TCSETA, &tio);
  334. #endif
  335. #ifdef USE_SGTTY
  336.     struct sgttyb sg;
  337.  
  338.     ioctl (0, TIOCGETP, &orig_sgtty);
  339.     sg = orig_sgtty;
  340.     sg.sg_flags &= ~ECHO;    /* do our own echoing */
  341.     sg.sg_flags &= ~CRMOD;    /* leave CR and LF unchanged */
  342.     sg.sg_flags |= XTABS;    /* no tabs with termcap */
  343.     sg.sg_flags |= CBREAK;    /* wake up on each char but can still kill */
  344.     ioctl (0, TIOCSETP, &sg);
  345. #endif
  346.     ttysetup = 1;
  347. }
  348. /* end of #ifdef UNIX */
  349. #endif
  350.  
  351. #ifdef TURBO_C
  352. #ifdef USE_ANSISYS
  353. #define    ESC    '\033'
  354. /* position cursor.
  355.  * (ANSI: ESC [ r ; c f) (r/c are numbers given in ASCII digits)
  356.  */
  357. c_pos (r, c)
  358. int r, c;
  359. {
  360.     printf ("%c[%d;%df", ESC, r, c);
  361. }
  362.  
  363. /* erase entire screen. (ANSI: ESC [ 2 J) */
  364. c_erase()
  365. {
  366.     printf ("%c[2J", ESC);
  367. }
  368.  
  369. /* erase to end of line. (ANSI: ESC [ K) */
  370. c_eol()
  371. {
  372.     printf ("%c[K", ESC);
  373. }
  374. #else
  375. #include <dos.h>   
  376. union REGS rg;
  377.  
  378. /* position cursor.
  379.  */
  380. c_pos (r, c)
  381. int r, c;
  382. {
  383.         rg.h.ah = 2;
  384.         rg.h.bh = 0;
  385.         rg.h.dh = r-1;
  386.         rg.h.dl = c-1;
  387.         int86(16,&rg,&rg);
  388. }
  389.  
  390. /* erase entire screen.  */
  391. c_erase()
  392. {
  393.         int cur_cursor, i;
  394.         rg.h.ah = 3;
  395.         rg.h.bh = 0;
  396.         int86(16,&rg,&rg);
  397.         cur_cursor = rg.x.dx;
  398.         for(i = 0; i < 25; i++){
  399.             c_pos(i+1,1);
  400.             rg.h.ah = 10;
  401.             rg.h.bh = 0;
  402.             rg.h.al = 32;
  403.             rg.x.cx = 80;
  404.             int86(16,&rg,&rg);
  405.         }
  406.         rg.h.ah = 2;
  407.         rg.h.bh = 0;
  408.         rg.x.dx = cur_cursor;
  409.         int86(16,&rg,&rg);
  410.         
  411. }
  412.  
  413. /* erase to end of line.*/
  414. c_eol()
  415. {
  416.         int cur_cursor, i;
  417.         rg.h.ah = 3;
  418.         rg.h.bh = 0;
  419.         int86(16,&rg,&rg);
  420.         cur_cursor = rg.x.dx;
  421.         rg.h.ah = 10;
  422.         rg.h.bh = 0;
  423.         rg.h.al = 32;
  424.         rg.x.cx = 80 - rg.h.dl;
  425.         int86(16,&rg,&rg);
  426.         rg.h.ah = 2;
  427.         rg.h.bh = 0;
  428.         rg.x.dx = cur_cursor;
  429.         int86(16,&rg,&rg);
  430.  
  431. }
  432. #endif
  433.  
  434. /* return 0 if there is a char that may be read without blocking, else -1 */
  435. chk_char()
  436. {
  437.     return (kbhit() == 0 ? -1 : 0);
  438. }
  439.  
  440. /* read the next char, blocking if necessary, and return it. don't echo.
  441.  * map the arrow keys if we can too into hjkl
  442.  */
  443. read_char()
  444. {
  445.     int c;
  446.     fflush (stdout);
  447.     c = getch();
  448.     if (c == 0) {
  449.         /* get scan code; convert to direction hjkl if possible */
  450.         c = getch();
  451.         switch (c) {
  452.         case 0x4b: c = 'h'; break;
  453.         case 0x50: c = 'j'; break;
  454.         case 0x48: c = 'k'; break;
  455.         case 0x4d: c = 'l'; break;
  456.         }
  457.     }
  458.     return (c);
  459. }
  460.  
  461. /* do whatever might be necessary to get the screen and/or tty back into shape.
  462.  */
  463. byetty()
  464. {
  465. }
  466. /* end of #ifdef TURBO_C */
  467. #endif
  468.  
  469. #ifdef VMS
  470. #include <string.h>
  471. #include <iodef.h>
  472. #include <descrip.h>
  473. #include <dvidef.h>
  474. #include <smgtrmptr.h>
  475. #include <starlet.h>
  476. #include <lib$routines.h>
  477. #include <smg$routines.h>
  478.  
  479. /* Structured types for use in system calls */
  480. typedef struct{
  481.     unsigned short status;
  482.     unsigned short count;
  483.     unsigned int info;
  484. } io_status_block;
  485. typedef struct{
  486.     unsigned short buffer_length;
  487.     unsigned short item_code;
  488.     void *buffer_address;
  489.     unsigned short *return_length_address;
  490.     unsigned long terminator;
  491. } item_list;
  492.  
  493. static unsigned short ttchan = 0; /* channel number for terminal    */
  494. volatile static io_status_block iosb; /* I/O status block for operation */
  495.                                       /* currently in progress          */
  496. volatile static unsigned char input_buf; /* buffer to recieve input charac-*/
  497.                                          /* ter when operation completes   */
  498. static void *term_entry;          /* pointer to TERMTABLE entry     */
  499. #define MAXCAP 10
  500. static char ce[MAXCAP];           /* ce and cl capability strings for  */
  501. static char cl[MAXCAP];           /* this terminal type                */
  502.  
  503. /* Declaration of special keys to be recoqnized on input */
  504. /* Number of special keys defined */
  505. #define MAXKEY 4
  506. /* TERMTABLE capability codes for the keys */
  507. static long capcode[MAXKEY] = {SMG$K_KEY_UP_ARROW,SMG$K_KEY_DOWN_ARROW,
  508.     SMG$K_KEY_RIGHT_ARROW,SMG$K_KEY_LEFT_ARROW};
  509. /* character codes to be returned by read_char when a special key is presssed */
  510. static int retcode[MAXKEY] = {'k','j','l','h'};
  511. /* the actual capability strings from the key */
  512. static char keycap[MAXKEY][MAXCAP];
  513.  
  514. static char special_buffer[MAXCAP];   /* buffer for reading special key */
  515. static int chars_in_buffer;           /* number of characters in buffer */
  516.  
  517. /* set up the structures for this I/O module */
  518. inittt()
  519. {
  520.     unsigned int status;   /* system routine return status */
  521.     $DESCRIPTOR(tt,"TT");  /* terminal name */
  522.     item_list itmlst;      /* item list for $getdvi obtaining term type */
  523.     unsigned long devtype; /* terminal type returned form $getdvi */
  524.     unsigned short retlen; /* return length from $getdvi */
  525.     unsigned long lenret;  /* return length from smg$get_term_data */
  526.     unsigned long maxlen;  /* maximum return length */
  527.     unsigned long cap_code;/* capability code */
  528. #define MAXINIT 20
  529.     char init_string[MAXINIT];/* string to initialize terminal */
  530.     int key;
  531.  
  532.     /* Assign a channel to the terminal */
  533.     if (!((status = sys$assign(&tt,&ttchan,0,0))&1)) lib$signal(status);
  534.  
  535.     /* Get terminal type. Note that it is possible to use the same
  536.      * iosb at this stage, because no I/O is initiated yet.
  537.      */
  538.     itmlst.buffer_length = 4;
  539.     itmlst.item_code = DVI$_DEVTYPE;
  540.     itmlst.buffer_address = &devtype;
  541.     itmlst.return_length_address = &retlen;
  542.     itmlst.terminator = 0;
  543.     if (!((status = sys$getdviw(0,ttchan,0,&itmlst,&iosb,0,0,0))&1))
  544.         lib$signal(status);
  545.     if (!(iosb.status&1)) lib$signal(iosb.status);
  546.  
  547.     /* Get the TERMTABLE entry corresponding to the terminal type */
  548.     if (!((status = smg$init_term_table_by_type(&devtype,
  549.         &term_entry))&1)) lib$signal(status);
  550.  
  551.     /* Get the initialisation string and initialize terminal */
  552.     cap_code = SMG$K_INIT_STRING;
  553.     maxlen = MAXINIT - 1;
  554.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  555.         &lenret,init_string))&1)) lib$signal(status);
  556.     init_string[lenret] = '\0';
  557.     fputs(init_string,stdout);
  558.     fflush(stdout);
  559.  
  560.     /* Get ce and cl capabilities, these are static */
  561.     cap_code = SMG$K_ERASE_TO_END_LINE;
  562.     maxlen = MAXCAP-1;
  563.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  564.         &lenret,ce))&1)) lib$signal(status);
  565.     ce[lenret] = '\0';
  566.  
  567.     cap_code = SMG$K_ERASE_WHOLE_DISPLAY;
  568.     maxlen = MAXCAP-1;
  569.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  570.         &lenret,cl))&1)) lib$signal(status);
  571.     cl[lenret] = '\0';
  572.  
  573.     /* Here one could obtain line drawing sequences, please feel free
  574.        to implement it ... */
  575.  
  576.     /* Get special keys to be recoqnized on input */
  577.     for (key = 0;key<MAXKEY;key++)
  578.     {
  579.         maxlen = MAXCAP-1;
  580.         if (!((status = smg$get_term_data(&term_entry,&capcode[key],
  581.             &maxlen,&lenret,keycap[key]))&1)) lib$signal(status);
  582.         keycap[key][lenret] = '\0';
  583.     }
  584.  
  585.     /* Initiate first input operation, NOECHO.
  586.      * NOFILTR allows any character to get through, this makes it
  587.      * possible to implement arrow recoqnition, and also makes
  588.      * DEL and BS get through.
  589.      * We don't wait for the operation to complete.
  590.      * Note that stdout has already been fflush'ed above.
  591.      */
  592.     if (!((status = sys$qio(0,ttchan,
  593.         IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
  594.         &iosb,0,0,&input_buf,1,0,0,0,0))&1)) lib$signal(status);
  595.  
  596.     /* Initialise special key buffer */
  597.     chars_in_buffer = 0;
  598. } /* inittt */
  599.  
  600.  
  601. /* return 0 if there is a char that may be read without blocking, else -1 */
  602. chk_char()
  603. {
  604.     if (!ttchan) inittt();
  605.  
  606.         return ( chars_in_buffer != 0 ? 0 :(iosb.status == 0 ? -1 : 0));
  607. }
  608.  
  609. /* read the next char, blocking if necessary, and return it. don't echo.
  610.  * map the arrow keys if we can too into hjkl
  611.  */
  612. read_char()
  613. {
  614.     unsigned int status;
  615.     int buf;
  616.     int i;
  617.     int found_key;
  618.     int key;
  619.     int this_len;
  620.     int match;
  621.  
  622.     if (!ttchan) inittt();
  623.  
  624.     /* If we attempted to read an special key previously, there are characters
  625.      * left in the buffer, return these before doing more I/O
  626.      */
  627.     if (chars_in_buffer!=0){
  628.         buf = special_buffer[0];
  629.         chars_in_buffer--;
  630.         for (i = 0;i<chars_in_buffer;i++)
  631.         {
  632.             special_buffer[i] = special_buffer[i+1];
  633.         }
  634.         special_buffer[chars_in_buffer] = '\0';
  635.     }
  636.     else {
  637.  
  638.         /* Loop over characters read, the loop is terminated when the
  639.          * characters read so far do not match any of the special keys
  640.          * or when the characters read so far is identical to one of
  641.          * the special keys.
  642.          */
  643.  
  644.         do
  645.         {
  646.             /* Wait for I/O to complete */
  647.             if (!((status = sys$synch(0,&iosb))&1)) lib$signal(status);
  648.             special_buffer[chars_in_buffer] = input_buf;
  649.             chars_in_buffer++;
  650.             special_buffer[chars_in_buffer] = '\0';
  651.  
  652.             /* Initiate next input operation */
  653.             fflush (stdout);
  654.             if (!((status = sys$qio(0,ttchan,
  655.                 IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
  656.                 &iosb,0,0,&input_buf,1,0,0,0,0))&1)) lib$signal(status);
  657.  
  658.  
  659.             /* Check for match with all special strings */
  660.             match = 0;
  661.             found_key = MAXKEY;
  662.             for (key = 0;key<MAXKEY;key++)
  663.             {
  664.                 this_len = strlen(keycap[key]);
  665.                 if (this_len<chars_in_buffer) continue;
  666.                 if (!strncmp(keycap[key],special_buffer,chars_in_buffer)){
  667.                     match = -1;
  668.                     if (this_len == chars_in_buffer){
  669.                         found_key = key;
  670.                         break;
  671.                     }
  672.                 }
  673.             }
  674.         }
  675.         while (match && (found_key == MAXKEY));
  676.  
  677.         /* If one of the keys matches the input string, return the
  678.          * corresponding  key code
  679.          */
  680.         if (found_key != MAXKEY)
  681.         {
  682.             buf = retcode[found_key];
  683.             chars_in_buffer = 0;
  684.         }
  685.         else /* return first character and store the rest in the buffer */
  686.         {
  687.             buf = special_buffer[0];
  688.             chars_in_buffer--;
  689.             for (i = 0;i<chars_in_buffer;i++)
  690.             {
  691.                 special_buffer[i] = special_buffer[i+1];
  692.             }
  693.         }
  694.         special_buffer[chars_in_buffer] = '\0';
  695.     }
  696.     return(buf);
  697. }
  698.  
  699. /* do whatever might be necessary to get the screen and/or tty back into shape.
  700.  */
  701. byetty()
  702. {
  703.     unsigned int status;
  704.  
  705.     if (ttchan)
  706.     {
  707.         /* There is no string in SMG to send to the terminal when
  708.          * terminating, one could clear the screen, move the cursor to
  709.          * the last line, or whatever. This program clears the screen
  710.          * anyway before calling this routine, so we do nothing.
  711.          */
  712.  
  713.  
  714.  
  715.         /* The following is not really neccessary, it will be done at program
  716.          * termination anyway, but if someone tries to use the I/O routines agai
  717.    n
  718.          * it might prove useful...
  719.          */
  720.         if (!((status = smg$del_term_table())&1)) lib$signal(status);
  721.         if (!((status = sys$dassgn(ttchan))&1)) lib$signal(status);
  722.         /* This also cancels any outstanding I/O on the channel */
  723.         ttchan = 0; /* marks terminal I/O as not initialized */
  724.     }
  725. }
  726.  
  727. /* position cursor. */
  728. c_pos (r, c)
  729. int r, c;
  730. {
  731.     unsigned long vector[3]; /* argument vector (position)   */
  732.     unsigned long status;    /* system service return status */
  733.     long lenret;             /* length of returned string    */
  734.     long maxlen;             /* maximum return length        */
  735.     unsigned long capcode;   /* capability code              */
  736.     char seq[2*MAXCAP];      /* returned string              */
  737.  
  738.     if (!ttchan) inittt();
  739.  
  740.     /* Set cursor depends on the position, therefore we have to call
  741.      * get_term_data for each operation
  742.      */
  743.     vector[0] = 2;
  744.     vector[1] = r;
  745.     vector[2] = c;
  746.     capcode = SMG$K_SET_CURSOR_ABS;
  747.     maxlen = 2*MAXCAP-1;
  748.     if (!((status = smg$get_term_data(&term_entry,&capcode,&maxlen,
  749.         &lenret,seq,vector))&1)) lib$signal(status);
  750.     seq[lenret] = '\0';
  751.  
  752.     fputs(seq,stdout);
  753. }
  754.  
  755. /* erase entire screen. */
  756. c_erase()
  757. {
  758.     if (!ttchan) inittt();
  759.  
  760.     fputs(cl,stdout);
  761. }
  762.  
  763. /* erase to end of line. */
  764. c_eol()
  765. {
  766.     if (!ttchan) inittt();
  767.  
  768.     fputs(ce,stdout);
  769. }
  770. /* end of #ifdef VMS */
  771. #endif
  772.  
  773. /* read up to max chars into buf, with cannonization.
  774.  * add trailing '\0' (buf is really max+1 chars long).
  775.  * return count of chars read (not counting '\0').
  776.  * assume cursor is already positioned as desired.
  777.  * if type END when n==0 then return -1.
  778.  */
  779. read_line (buf, max)
  780. char buf[];
  781. int max;
  782. {
  783.     static char erase[] = "\b \b";
  784.     int n, c;
  785.     int done;
  786.  
  787. #ifdef UNIX
  788.     if (!ttysetup) setuptty();
  789. #endif
  790.  
  791.     for (done = 0, n = 0; !done; )
  792.         switch (c = read_char()) {    /* does not echo */
  793.         case cntrl('h'):    /* backspace or */
  794.         case 0177:        /* delete are each char erase */
  795.         if (n > 0) {
  796.             fputs (erase, stdout);
  797.             n -= 1;
  798.         }
  799.         break;
  800.         case cntrl('u'):        /* line erase */
  801.         while (n > 0) {
  802.             fputs (erase, stdout);
  803.             n -= 1;
  804.         }
  805.         break;
  806.         case '\r':    /* EOL */
  807.         done++;
  808.         break;
  809.         default:            /* echo and store, if ok */
  810.         if (n == 0 && c == END)
  811.             return (-1);
  812.         if (n >= max)
  813.             putchar (cntrl('g'));
  814.         else if (isprint(c)) {
  815.             putchar (c);
  816.             buf[n++] = c;
  817.         }
  818.         }
  819.  
  820.     buf[n] = '\0';
  821.     return (n);
  822. }
  823.